//
// Created by RandBII on 2020/12/27.
//

#include "OpenSLRender.h"


void OpenSLRender::Init() {

    int result = -1;

    do {
        result = CreateEngine();
        if (result != SL_RESULT_SUCCESS) {
            LOG_E("CreateEngine error ");
            break;
        }
        result = CreateOutputMixer();
        if (result != SL_RESULT_SUCCESS) {
            LOG_E("CreateOutputMixer error ");
            break;
        }
        result = CreateAudioPlayer();
        if (result != SL_RESULT_SUCCESS) {
            LOG_E("CreateAudioPlayer error ");
            break;
        }
        mThread = new std::thread(CreateSLWaitingThread, this);

    } while (false);
    if (result != SL_RESULT_SUCCESS) {
        LOG_E("OpenSLRender Init() error  result is %d", result);
        UnInit();
    }

}

void OpenSLRender::ClearAudioCache() {

    std::unique_lock<std::mutex> lock(mMutex);

    for (int i = 0; i < mAudioFrameQueue.size(); ++i) {
        AudioFrame *audioFrame = mAudioFrameQueue.front();
        mAudioFrameQueue.pop();
        delete audioFrame;
    }

}

void OpenSLRender::RenderAudioFrame(uint8_t *pData, int dataSize) {
    if (mAudioPlayerPlay) {
        if (pData && dataSize > 0) {
            while (GetAudioFrameQueueSize() >= MAX_BUFFER_QUEUE_SIZE && !mExit) {
                std::this_thread::sleep_for(std::chrono::milliseconds(15));
            }
            std::unique_lock<std::mutex> lock(mMutex);
            auto *audioFrame = new AudioFrame(pData, dataSize);
            mAudioFrameQueue.push(audioFrame);
            mCond.notify_all();
            lock.unlock();
        }
    }
}

void OpenSLRender::UnInit() {

    if (mAudioPlayerPlay) {
        (*mAudioPlayerPlay)->SetPlayState(mAudioPlayerPlay, SL_PLAYSTATE_STOPPED);
        mAudioPlayerPlay = nullptr;
    }
    std::unique_lock<std::mutex> lock(mMutex);
    mExit = true;
    mCond.notify_all();
    lock.unlock();
    if (mAudioPlayerObj) {
        (*mAudioPlayerObj)->Destroy(mAudioPlayerObj);
        mAudioPlayerObj = nullptr;
        mBufferQueue = nullptr;
    }

    if (mOutputMixObj) {
        (*mOutputMixObj)->Destroy(mOutputMixObj);
        mOutputMixObj = nullptr;
    }

    if (mEngineObj) {
        (*mEngineObj)->Destroy(mEngineObj);
        mEngineObj = nullptr;
    }

    lock.lock();
    for (int i = 0; i < mAudioFrameQueue.size(); ++i) {
        AudioFrame *audioFrame = mAudioFrameQueue.front();
        mAudioFrameQueue.pop();
        delete audioFrame;
    }
    lock.unlock();
    if (!mThread) {
        mThread->join();
        delete mThread;
        mThread = nullptr;
    }

    AudioGLRender::ReleaseInstance();


}

int OpenSLRender::CreateEngine() {

    SLresult result = SL_RESULT_SUCCESS;

    do {
        result = slCreateEngine(&mEngineObj, 0,
                                nullptr, 0,
                                nullptr, nullptr);
        if (result != SL_RESULT_SUCCESS) {
            LOG_E("slCrateEngine error");
            break;
        }
        result = (*mEngineObj)->Realize(mEngineObj, SL_BOOLEAN_FALSE);
        if (result != SL_RESULT_SUCCESS) {
            LOG_E("mEngineObj Realize error result is  %d", result);
            break;
        }

        result = (*mEngineObj)->GetInterface(mEngineObj, SL_IID_ENGINE, &mEngineEngine);
        if (result != SL_RESULT_SUCCESS) {
            LOG_E("mEngineObj GetInterface error   result is %d", result);
            break;
        }
    } while (false);

    return result;
}

int OpenSLRender::CreateOutputMixer() {
    SLresult result = SL_RESULT_SUCCESS;
    do {
        const SLInterfaceID mids[1] = {SL_IID_ENVIRONMENTALREVERB};
        const SLboolean mreq[1] = {SL_BOOLEAN_FALSE};
        result = (*mEngineEngine)->CreateOutputMix(mEngineEngine, &mOutputMixObj, 1, mids, mreq);
        if (result != SL_RESULT_SUCCESS) {
            LOG_E("OpenSLRender CreateOutputMixer error ");
            break;
        }
        result = (*mOutputMixObj)->Realize(mOutputMixObj, SL_BOOLEAN_FALSE);
        if (result != SL_RESULT_SUCCESS) {
            LOG_E("OpenSLRender CreateOutputMixer failed  result %d", result);
            break;
        }
    } while (false);
    return result;
}

int OpenSLRender::CreateAudioPlayer() {
//    坑 使用SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE
    SLDataLocator_AndroidSimpleBufferQueue androidQueue = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
                                                           2};
    SLDataFormat_PCM pcm = {
            SL_DATAFORMAT_PCM,
            (SLuint32) 2,
            SL_SAMPLINGRATE_44_1,
            SL_PCMSAMPLEFORMAT_FIXED_16,
            SL_PCMSAMPLEFORMAT_FIXED_16,
            SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
            SL_BYTEORDER_LITTLEENDIAN
    };
    SLDataSource source = {&androidQueue, &pcm};
    SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, mOutputMixObj};
    SLDataSink slDataSink = {&outputMix, nullptr};

    const SLInterfaceID ids[3] = {SL_IID_BUFFERQUEUE, SL_IID_EFFECTSEND, SL_IID_VOLUME};
    const SLboolean req[3] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};

    SLresult result;

    do {
        result = (*mEngineEngine)->CreateAudioPlayer(mEngineEngine, &mAudioPlayerObj, &source,
                                                     &slDataSink, 3, ids, req);
        if (result != SL_RESULT_SUCCESS) {
            LOG_E("CreateAudioPlayer error result is %d", result);
            break;
        }
        result = (*mAudioPlayerObj)->Realize(mAudioPlayerObj, SL_BOOLEAN_FALSE);
        if (result != SL_RESULT_SUCCESS) {
            LOG_E("CreateAudioPlayer  Realize error result is %d", result);
            break;
        }
        result = (*mAudioPlayerObj)->GetInterface(mAudioPlayerObj, SL_IID_PLAY, &mAudioPlayerPlay);
        if (result != SL_RESULT_SUCCESS) {
            LOG_E("CreateAudioPlayer GetInterface SL_IID_PLAY error %d", result);
            break;
        }
        result = (*mAudioPlayerObj)->GetInterface(mAudioPlayerObj, SL_IID_BUFFERQUEUE,
                                                  &mBufferQueue);
        if (result != SL_RESULT_SUCCESS) {
            LOG_E("CraeteAudioPlayer GetInterface SL_IID_BUFFERQUEUE %d ", result);
            break;
        }

        result = (*mBufferQueue)->RegisterCallback(mBufferQueue, AudioPlayerCallback, this);
        if (result != SL_RESULT_SUCCESS) {
            LOG_E("CreateAudioPlayer RegisterCallback error  result is %d", result);
            break;
        }

        result = (*mAudioPlayerObj)->GetInterface(mAudioPlayerObj, SL_IID_VOLUME,
                                                  &mAudioPlayerVolume);
        if (result != SL_RESULT_SUCCESS) {
            LOG_E("CreateAudioPlayer GetInterface SL_IID_VOLUME is error %d", result);
            break;
        }

    } while (false);

    return result;

}

int OpenSLRender::GetAudioFrameQueueSize() {
    std::unique_lock<std::mutex> lock(mMutex);

    return mAudioFrameQueue.size();
}


void OpenSLRender::StartRender() {
    while (GetAudioFrameQueueSize() < MAX_BUFFER_QUEUE_SIZE && !mExit) {
        std::unique_lock<std::mutex> lock(mMutex);
        mCond.wait_for(lock, std::chrono::milliseconds(10));
        lock.unlock();
    }
    (*mAudioPlayerPlay)->SetPlayState(mAudioPlayerPlay, SL_PLAYSTATE_PLAYING);
    AudioPlayerCallback(mBufferQueue, this);


}

void OpenSLRender::HandleAudioFrameQueue() {
    if (!mAudioPlayerPlay) return;
    while (GetAudioFrameQueueSize() < MAX_BUFFER_QUEUE_SIZE && !mExit) {
        std::unique_lock<std::mutex> lock(mMutex);
        mCond.wait_for(lock, std::chrono::milliseconds(10));
    }
    std::unique_lock<std::mutex> lock(mMutex);
    AudioFrame *audioFrame = mAudioFrameQueue.front();
    if (audioFrame && mAudioPlayerPlay) {
        SLresult result = (*mBufferQueue)->Enqueue(mBufferQueue, audioFrame->data,
                                                   audioFrame->dataSize);
        if (result == SL_RESULT_SUCCESS) {
            AudioGLRender::GetInstance()->UpdateAudioFrame(audioFrame);
            mAudioFrameQueue.pop();
            delete audioFrame;
        }
    }
    lock.unlock();

}

void OpenSLRender::CreateSLWaitingThread(OpenSLRender *slRender) {
    slRender->StartRender();
}

void OpenSLRender::AudioPlayerCallback(SLAndroidSimpleBufferQueueItf bufferQueue, void *context) {
    auto *slRender = static_cast<OpenSLRender *>(context);
    slRender->HandleAudioFrameQueue();
}
